package com.ld.security.common.elastic.util;


import com.ld.security.common.core.base.po.*;
import com.ld.security.common.core.util.ReflectionUtil;
import com.ld.security.common.elastic.base.anno.EsKey;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.ReflectionUtils;

import java.util.*;
import java.util.stream.Collectors;

public class ElasticUtil {

    private static <E extends SuperPo> BoolQueryBuilder newSearchQuery(E query) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(boolQueryBuilder == null){
            return  boolQueryBuilder;
        }
        ReflectionUtil.applyAllDeclaredFields(query, EsKey.class, (anno, name, val) -> {
            if (val == null) {
                return;
            }else if(val instanceof Date){
                val = ((Date)val).getTime();
            }else if(val instanceof Collection){
                Collection col = (Collection)val;
                if(col.isEmpty()){
                    return;
                }

                if(val instanceof List){
                    List list = (List) val;
                    //日期格式的,华为数字
                    if (list.get(0) instanceof Date){
                        val = list.stream().map(v -> {
                            Date d = (Date)v;
                            return d.getTime();
                        }).collect(Collectors.toList());
                    }
                }
            }
            QueryBuilder queryBuilder =null;
            switch (anno.value()) {

                case eq: {
                    queryBuilder = QueryBuilders.termsQuery(name, val);

                    break;
                }
                case in: {
                    queryBuilder = QueryBuilders.termsQuery(name, val);

                    break;
                }
                case like: {
                    queryBuilder = QueryBuilders.matchQuery(name, val);
                    break;
                }
                case like_left:{
                    queryBuilder =QueryBuilders.wildcardQuery(name, "*"+val);
                    break;
                }
                case like_right:{
                    queryBuilder =QueryBuilders.wildcardQuery(name, val+"*");
                    break;
                }
                case between: {
                    List<Object> list = (List) val;

                    queryBuilder =  QueryBuilders.rangeQuery(name).gte(list.get(0)).lte(list.get(1));

                    break;
                }
                case ge: {
                    queryBuilder = QueryBuilders.rangeQuery(name).gte(val);
                    break;
                }
                case le: {
                    queryBuilder = QueryBuilders.rangeQuery(name).lte(val);
                    break;
                }

            }

            queryBuilder.boost(anno.boost());
            if(anno.and()){
                boolQueryBuilder.must(queryBuilder);
            }else{
                boolQueryBuilder.should(queryBuilder);
            }
        });

        return boolQueryBuilder;
    }

    private static <E extends SuperPo> NativeSearchQueryBuilder newAllSearchQuery(SuperPo query, boolean hi) {



        BoolQueryBuilder boolQueryBuilder = newSearchQuery(query);
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        LinkedList<HighlightBuilder.Field> his = new LinkedList<>();

        ReflectionUtil.applyAllDeclaredFields(query, EsKey.class, (anno, name, val) -> {
            if (val == null) {
                return;
            }
            if (anno.highlight()) {
                his.add(new HighlightBuilder.Field(name).preTags(anno.preTags()).postTags(anno.postTags()));
            }
        });

        if (!his.isEmpty()) {
            HighlightBuilder.Field[] his2 = his.toArray(new HighlightBuilder.Field[his.size()]);
            nativeSearchQueryBuilder.withHighlightFields(his2);
        }


        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);


        return nativeSearchQueryBuilder;
    }

    /**
     * 分页查询
     * @param po
     * @param hi
     * @param <E>
     * @return
     */
    public static <E extends SuperPo> NativeSearchQuery pageNativeSearchQuery(SuperPo po, boolean hi) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = null;

        SuperPo q = po;

        if(po instanceof QueryPo){
            QueryPo<SuperPo> queryPo = (QueryPo)po;
            q =queryPo.getQuery();
        }

        nativeSearchQueryBuilder = newAllSearchQuery(q, hi);

        if(po instanceof OrderByPo){
            OrderByPo pp = (OrderByPo)po;
            if (pp.isAsc()) {
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(pp.getOrderBy())
                        .order(SortOrder.ASC));
            } else {
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(pp.getOrderBy())
                        .order(SortOrder.DESC));
            }
        }

        if(po instanceof LimitPo){
            LimitPo limitPo = (LimitPo)po;
            nativeSearchQueryBuilder.withPageable(PageRequest.of(limitPo.getPageNumber()-1, limitPo.getPageSize()));
        }




        return nativeSearchQueryBuilder.build();

    }

    /**
     * 查询所特有
     * @param po
     * @param hi
     * @param <E>
     * @return
     */
    public static <E extends SuperPo> NativeSearchQuery listNativeSearchQuery(ListPo<E> po, boolean hi) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = newAllSearchQuery(po, hi);
        return nativeSearchQueryBuilder.build();
    }


    public static SearchResultMapper newSearchResultMapper() {
        return new SimpSearchResultMapper();
    }

}
